use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher, event::ModifyKind, EventKind};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use tauri::{AppHandle, Emitter};
use tokio::sync::mpsc;

#[derive(Debug)]
pub enum WatcherCommand {
    Watch(String),
    Stop(String),
    // stop all watchers
    StopAll,
}

// --- 2. 定义前端事件结构体 ---
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileSystemEvent {
    pub event_type: String,
    pub path: String,
    pub old_path: Option<String>,
}

// --- 3. 定义 Actor ---
pub struct FileWatcherManager {
    watchers: HashMap<String, WatcherHandle>,
    app_handle: AppHandle,
    debounce_map: Arc<Mutex<HashMap<PathBuf, Instant>>>,
    shutdown_tx: Option<tokio::sync::oneshot::Sender<()>>,
}

struct WatcherHandle {
    #[allow(dead_code)]
    watcher: RecommendedWatcher,
    task_handle: tokio::task::JoinHandle<()>,
    shutdown_tx: tokio::sync::oneshot::Sender<()>,
    // store the watched path for explicit unwatch
    path: PathBuf,
}

impl FileWatcherManager {
    // 构造函数
    pub fn new(app_handle: AppHandle) -> Self {
        Self {
            watchers: HashMap::new(),
            app_handle,
            debounce_map: Arc::new(Mutex::new(HashMap::new())),
            shutdown_tx: None,
        }
    }
    pub async fn run(mut self, mut rx: mpsc::UnboundedReceiver<WatcherCommand>) {
        let (shutdown_tx, mut shutdown_rx) = tokio::sync::oneshot::channel();
        self.shutdown_tx = Some(shutdown_tx);
        
        loop {
            tokio::select! {
                command = rx.recv() => {
                    match command {
                        Some(WatcherCommand::Watch(path)) => {
                            if let Err(e) = self.watch_directory(&path).await {
                                eprintln!("[FileWatcher] Error watching directory '{}': {}", path, e);
                            }
                        }
                        Some(WatcherCommand::Stop(path)) => {
                            self.stop_watching(&path);
                        }
                        Some(WatcherCommand::StopAll) => {
                            self.shutdown_all().await;
                        }
                        None => break, // Channel closed
                    }
                }
                _ = &mut shutdown_rx => {
                    // Graceful shutdown
                    self.shutdown_all().await;
                    break;
                }
            }
        }
    }
    
    async fn shutdown_all(&mut self) {
        for (_, mut handle) in self.watchers.drain() {
            // try unwatch before dropping watcher
            let _ = handle.watcher.unwatch(&handle.path);
            let _ = handle.shutdown_tx.send(());
            let _ = handle.task_handle.await;
        }
        
        // 清理防抖映射表
        if let Ok(mut map) = self.debounce_map.lock() {
            map.clear();
        }
    }

    // 内部逻辑：开始监控
    async fn watch_directory(&mut self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
        let path_buf = PathBuf::from(path);
        if !path_buf.exists() || !path_buf.is_dir() {
            return Err("Path does not exist or is not a directory".into());
        }

        // 如果已在监控，先停止
        self.stop_watching(path);

        let (event_tx, mut event_rx) = mpsc::unbounded_channel();
        let (task_shutdown_tx, mut task_shutdown_rx) = tokio::sync::oneshot::channel();
        let app_handle = self.app_handle.clone();
        let debounce_map = self.debounce_map.clone();

        let mut watcher = RecommendedWatcher::new(
            move |res: Result<Event, notify::Error>| {
                if let Ok(event) = res {
                    let _ = event_tx.send(event);
                }
            },
            Config::default().with_poll_interval(Duration::from_millis(100)),
        )?;

        watcher.watch(&path_buf, RecursiveMode::Recursive)?;

        // 启动独立的事件处理任务
        let task_handle = tokio::spawn(async move {
            loop {
                tokio::select! {
                    event = event_rx.recv() => {
                        if let Some(event) = event {
                            Self::handle_file_event(event, &app_handle, &debounce_map).await;
                        } else {
                            break;
                        }
                    }
                    _ = &mut task_shutdown_rx => {
                        break;
                    }
                }
            }
        });

        // 正确存储 WatcherHandle
        let handle = WatcherHandle {
            watcher,
            task_handle,
            shutdown_tx: task_shutdown_tx,
            path: path_buf,
        };
        self.watchers.insert(path.to_string(), handle);

        Ok(())
    }

    // 内部逻辑：停止监控
    fn stop_watching(&mut self, path: &str) {
        if let Some(mut handle) = self.watchers.remove(path) {
            // try unwatch before dropping watcher
            let _ = handle.watcher.unwatch(&handle.path);
            let _ = handle.shutdown_tx.send(());
            // task_handle 会在 shutdown_all 或下次调用时被 await
        }
    }

    // 内部逻辑：处理并发送事件到前端
    async fn handle_file_event(
        event: Event,
        app_handle: &AppHandle,
        debounce_map: &Arc<Mutex<HashMap<PathBuf, Instant>>>,
    ) {
        // helper to filter noisy paths
        fn should_skip_path(p: &std::path::Path) -> bool {
            const SKIP_DIRS: [&str; 7] = [
                ".git",
                "node_modules",
                "target",
                "dist",
                ".angular",
                ".idea",
                ".vscode",
            ];
            p.components().any(|c| {
                if let std::path::Component::Normal(os) = c {
                    if let Some(s) = os.to_str() {
                        SKIP_DIRS.iter().any(|d| s.eq_ignore_ascii_case(d))
                    } else {
                        false
                    }
                } else {
                    false
                }
            })
        }

        let now = Instant::now();
        let should_emit = {
            // if event has no paths, skip
            if event.paths.is_empty() {
                return;
            }
            // if all paths are noisy, skip
            if event.paths.iter().all(|p| should_skip_path(p)) {
                return;
            }

            // Return early if no paths present in the event to avoid using a default empty key
            let first_path = match event.paths.first().cloned() {
                Some(p) => p,
                None => return,
            };

            let mut map = debounce_map
                .lock()
                .unwrap_or_else(|e| e.into_inner());

            if let Some(last_time) = map.get(&first_path) {
                if now.duration_since(*last_time) < Duration::from_millis(100) {
                    false
                } else {
                    map.insert(first_path, now);
                    true
                }
            } else {
                map.insert(first_path, now);
                true
            }
        };

        if !should_emit {
            return;
        }

        let fs_event = match event.kind {
            EventKind::Create(_) => event.paths.first().map(|p| FileSystemEvent {
                event_type: "create".to_string(),
                path: p.to_string_lossy().to_string(),
                old_path: None,
            }),
            EventKind::Remove(_) => event.paths.first().map(|p| FileSystemEvent {
                event_type: "delete".to_string(),
                path: p.to_string_lossy().to_string(),
                old_path: None,
            }),
            EventKind::Modify(ModifyKind::Name(_)) => {
                if event.paths.len() >= 2 {
                    Some(FileSystemEvent {
                        event_type: "rename".to_string(),
                        path: event.paths[1].to_string_lossy().to_string(),
                        old_path: Some(event.paths[0].to_string_lossy().to_string()),
                    })
                } else {
                    event.paths.first().map(|p| FileSystemEvent {
                        event_type: "modify".to_string(),
                        path: p.to_string_lossy().to_string(),
                        old_path: None,
                    })
                }
            },
            EventKind::Modify(_) => event.paths.first().map(|p| FileSystemEvent {
                event_type: "modify".to_string(),
                path: p.to_string_lossy().to_string(),
                old_path: None,
            }),
            _ => None,
        };

        if let Some(event_data) = fs_event {
            if let Err(e) = app_handle.emit("file-system-change", &event_data) {
                eprintln!("[FileWatcher] Failed to emit event: {}", e);
            }
        }

        // 定期清理过期的防抖记录
        static LAST_CLEANUP: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
        // Use a monotonic increasing timestamp in milliseconds since UNIX_EPOCH
        let now_millis: u64 = match std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
            Ok(d) => d.as_millis() as u64,
            Err(_) => 0,
        };

        if now_millis.saturating_sub(LAST_CLEANUP.load(std::sync::atomic::Ordering::Relaxed)) > 60_000 {
            if let Ok(mut map) = debounce_map.lock() {
                // Retain only entries updated within the last 10 seconds
                map.retain(|_, last_time| last_time.elapsed() < Duration::from_secs(10));
            }
            LAST_CLEANUP.store(now_millis, std::sync::atomic::Ordering::Relaxed);
        }
    }
}

// --- 4. Tauri 命令 ---
#[tauri::command]
pub async fn start_watching_directory(
    path: String,
    sender: tauri::State<'_, mpsc::UnboundedSender<WatcherCommand>>,
) -> Result<(), String> {
    sender.send(WatcherCommand::Watch(path)).map_err(|e| e.to_string())
}

#[tauri::command]
pub async fn stop_watching_directory(
    path: String,
    sender: tauri::State<'_, mpsc::UnboundedSender<WatcherCommand>>,
) -> Result<(), String> {
    sender.send(WatcherCommand::Stop(path)).map_err(|e| e.to_string())
}

#[tauri::command]
pub async fn stop_all_watching(
    sender: tauri::State<'_, mpsc::UnboundedSender<WatcherCommand>>,
) -> Result<(), String> {
    sender.send(WatcherCommand::StopAll).map_err(|e| e.to_string())
}